﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace TrafficSplitter
{
    public partial class Main : Form
    {
        private AdapterFunctionsRAS rasInterfaces;
        private AdapterFunctionsWMI adapterInterfaces;
        private List<VirtualAdapter> adapters;
        private VirtualInterface[] interfaces;
        private Boolean continueSetupRoutes;
        private ComboBox [] listOfComboboxes;
        private Button [] listOfActionButtons;
        private Button [] listOfShowRouteButtons;
        private Button [] listOfEditRouteButtons;
        private RadioButton[] listOfRadioButtonsLocalLists;
        private RadioButton[] listOfRadioButtonsAuxLists;
        private CheckBox [] listOfPersistentCheckboxes;
        private CheckBox[] listOfReconnectCheckboxes;
        private Config config;
        private Boolean showBalloon;
        private StringBuilder logBuffer;
        private Label[][] listOfStatsLabels;
        private long[][] listOfStatsValues;
        private static FileStream _file;
        private DynDNS ddns;

        #region Main function
        public Main()
        {
            InitializeComponent();

            Constants.main = this;
            this.Text = Application.ProductName + " v" + Application.ProductVersion;

            continueSetupRoutes = true;
            logBuffer = null;
            interfaces = new VirtualInterface[Constants.MAX_INTERFACES];
            rasInterfaces = new AdapterFunctionsRAS();
            adapterInterfaces = new AdapterFunctionsWMI();
            adapters = new List<VirtualAdapter>();
            listOfComboboxes = new ComboBox[Constants.MAX_INTERFACES] {cbInternational, cbLocal, cbAuxilary, cbAuxilary2, cbAuxilary3};
            listOfActionButtons = new Button[Constants.MAX_INTERFACES] {btnInternational, btnLocal, btnAuxilary, btnAuxilary2, btnAuxilary3};
            listOfShowRouteButtons = new Button[Constants.MAX_INTERFACES] {btnInternationalR, btnLocalR, btnAuxilaryR, btnAuxilary2R, btnAuxilary3R};
            listOfEditRouteButtons = new Button[Constants.MAX_INTERFACES] { btnEditIntRoutes, btnEditLocalRoutes, btnEditAuxRoutes, btnEditAux2Routes, btnEditAux3Routes };
            listOfPersistentCheckboxes = new CheckBox[Constants.MAX_INTERFACES] {chkPersistentInt, chkPersistentLocal, chkPersistentAux, chkPersistentAux2, chkPersistentAux3};
            listOfReconnectCheckboxes = new CheckBox[Constants.MAX_INTERFACES] { chkReconnectInt, chkReconnectLocal, chkReconnectAux, chkReconnectAux2, chkReconnectAux3 };
            listOfRadioButtonsLocalLists = new RadioButton[4] { rbLocalListLocalityDevs, rbLocalListMaxmind, rbLocalListSADC, rbLocalListNonMweb };
            listOfRadioButtonsAuxLists = new RadioButton[2] { rbAuxListMweb, rbAuxListWage };
            listOfStatsLabels = new Label[2][];
            listOfStatsValues = new long[4][];
            for (int i = 0; i < 4; i++)
            {
                listOfStatsValues[i] = new long[Constants.MAX_INTERFACES];
            }
            listOfStatsLabels[0] = new Label[Constants.MAX_INTERFACES] { lblStatsRecvInt, lblStatsRecvLocal, lblStatsRecvAux, lblStatsRecvAux2, lblStatsRecvAux3 };
            listOfStatsLabels[1] = new Label[Constants.MAX_INTERFACES] { lblStatsSentInt, lblStatsSentLocal, lblStatsSentAux, lblStatsSentAux2, lblStatsSentAux3 };
            config = new Config(string.Format(@"{0}\config.ini", Environment.CurrentDirectory), listOfComboboxes, listOfPersistentCheckboxes, listOfReconnectCheckboxes, chkAutoRouteSetup, chkMinimize2Tray, chkMinimizeOnStartup);

            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                interfaces[i] = new VirtualInterface(Constants.FILENAMES[i], (TS_INTERFACES)i, rasInterfaces, adapterInterfaces);
                interfaces[i].adapterstatechanged += new EventHandler(adapterStateChanged);
                interfaces[i].errorOnConnect += new EventHandler(errorOnConnect);
            }

            // Enumerate interfaces
            AdapterFunctionsNone noInterfaces = new AdapterFunctionsNone();
            noInterfaces.EnumerateInterfaces(adapters);
            rasInterfaces.EnumerateInterfaces(adapters);
            adapterInterfaces.EnumerateInterfaces(adapters);
            
            // Add interfaces to comboboxes
            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                ComboBox combobox = listOfComboboxes[i];
                foreach (VirtualAdapter adapter in adapters)
                {
                    combobox.Items.Add(adapter);
                }
            }

            // Auxilary interface selector for downloading WAGE/Mweb routes
            cbAuxRouteSelector.SelectedIndex = 0;

            // Dynamic DNS
            ddns = new DynDNS(string.Format(@"{0}\ddns.ini", Environment.CurrentDirectory),
                interfaces[(int)TS_INTERFACES.Local], chkDDNSEnabled, btnDDNSUdate, lblDDNSPreviousIP, lblDDNSChanged,
                rbDDNS1, rbDDNS2, tbDDNSUsername, tbDDNSPassword, tbDDNSHostnames, lblDDNSLastUpdated);

            // Notify tray icon
            notifyIcon1.BalloonTipText = "Double-click to show";  
            notifyIcon1.BalloonTipTitle = Application.ProductName;  
            notifyIcon1.Icon = this.Icon;
            notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
            notifyIcon1.Visible = false;
        }
        #endregion

        #region Delegates
        private delegate void AppendToLogCallback(LOG_TYPE type, String str);
        private delegate void StateChanged(object sender, EventArgs e);
        private delegate void OnConnectError(object sender, EventArgs e);
        #endregion

        #region Logging
        public void AppendToLog(LOG_TYPE type, String str)
        {
            if (this.InvokeRequired)
            {
                AppendToLogCallback cb = new AppendToLogCallback(AppendToLogInvoked);
                this.Invoke(cb, new object[] {type, str});
            }
            else
                AppendToLogInvoked(type, str);
        }

        public void AppendToLogBuffer(LOG_TYPE type, String str)
        {
            StringBuilder sb = CreateLogLine(type, str);
            if (logBuffer == null)
                logBuffer = new StringBuilder();

            logBuffer.Append(sb);
        }

        public void FlushLogBuffer()
        {
            if (this.InvokeRequired)
            {
                AppendToLogCallback cb = new AppendToLogCallback(FlushLogBufferInvoked);
                this.Invoke(cb, new object[] { LOG_TYPE.Info, null });
            }
            else
                FlushLogBufferInvoked(LOG_TYPE.Info, null);
        }

        public void FlushLogBufferInvoked(LOG_TYPE type, String str)
        {
            if (logBuffer != null)
            {
                if (_file != null)
                {
                    Encoding encoding = Encoding.ASCII;
                    byte[] data = encoding.GetBytes(logBuffer.ToString());
                    try
                    {
                        _file.Write(data, 0, data.Length);
                        _file.Flush();
                    }
                    catch (Exception ex)
                    {
                        StringBuilder tmp = CreateLogLine(LOG_TYPE.Error, ex.Message);
                        this.tbLogs.Text += tmp.ToString();
                    }
                }
                this.tbLogs.Text += logBuffer.ToString();
                logBuffer = null;
            }
        }

        private StringBuilder CreateLogLine (LOG_TYPE type, String str)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(DateTime.Now.ToShortDateString());
            sb.Append(" ");
            sb.Append(DateTime.Now.ToLongTimeString());
            sb.Append(" : [" );
            sb.Append(type.ToString().ToUpper());
            sb.Append("] ");
            sb.Append( str);
            sb.Append("\r\n");
            return sb;
        }

        private void AppendToLogInvoked(LOG_TYPE type, String str)
        {
            StringBuilder sb = CreateLogLine(type, str);
            if (_file == null)
            {
                try
                {
                    _file = File.OpenWrite(string.Format(@"{0}\debug.log", Environment.CurrentDirectory));
                }
                catch (Exception ex)
                {
                    StringBuilder tmp = CreateLogLine(LOG_TYPE.Error, ex.Message);
                    this.tbLogs.Text += tmp.ToString();
                }
            }
            string entry = sb.ToString();

            Encoding encoding = Encoding.ASCII;
            byte[] data = encoding.GetBytes(entry);
            try
            {
                _file.Write(data, 0, data.Length);
            }
            catch (Exception ex)
            {
                StringBuilder tmp = CreateLogLine(LOG_TYPE.Error, ex.Message);
                this.tbLogs.Text += tmp.ToString();
            }
            this.tbLogs.Text += entry;
        }
        #endregion

        #region Array bound functions
        private void ArraySetButtonText(int index, String str)
        {
            Button btn = listOfActionButtons[index];
            btn.Text = str;
        }

        private void ArraySetButtonEnabled(int index, Boolean enabled)
        {
            Button btn = listOfActionButtons[index];
            btn.Enabled = enabled;
        }

        private String ArrayGetComboBoxText(int index)
        {
            ComboBox cb = listOfComboboxes[index];
            if (cb.SelectedIndex < 0)
                return null;
        
            VirtualAdapter adapter = (VirtualAdapter)cb.SelectedItem;
            return adapter.GetEntryName();
        }

        private Int32 ArrayGetIndex(object sender)
        {
            if (sender is ComboBox)
            {
                for (int i = 0; i < Constants.MAX_INTERFACES; i++)
                {
                    if (sender == listOfComboboxes[i])
                        return i;
                }
            }
            else if (sender is Button)
            {
                for (int i = 0; i < Constants.MAX_INTERFACES; i++)
                {
                    if ((sender == listOfActionButtons[i]) 
                        || (sender == listOfShowRouteButtons[i])
                        || (sender == listOfEditRouteButtons[i]))
                        return i;
                }
            }
            else if (sender is RadioButton)
            {
                if (sender == rbLocalListLocalityDevs)
                {
                    for (int i = 0; i < listOfRadioButtonsLocalLists.Length; i++)
                    {
                        if (listOfRadioButtonsLocalLists[i].Checked == true)
                            return i;
                    }
                }
                else
                {
                    for (int i = 0; i < listOfRadioButtonsAuxLists.Length; i++)
                    {
                        if (listOfRadioButtonsAuxLists[i].Checked == true)
                            return i;
                    }
                }
            }
            return -1;
        }
        #endregion

        #region Events
        private void tabEntered(object sender, EventArgs e)
        {
            TabPage page = (TabPage)sender;
            Boolean isAdv = (page == tabAdv);

            this.gbAuxilary.Visible = isAdv;
            this.gbAuxilary2.Visible = isAdv;
            this.gbAuxilary3.Visible = isAdv;
            this.gbAddOrDelete.Visible = isAdv;
            page.Controls.Add(this.gbInterfaces);
            page.Controls.Add(this.gbOptions);
        }

        private void cb_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox cb = (ComboBox)sender;
            int index = ArrayGetIndex(cb);
            interfaces[index].UpdateAdapter((VirtualAdapter)cb.SelectedItem);
            listOfStatsValues[2][index] = 0;
            listOfStatsValues[3][index] = 0;
        }

        private void StateChangedInvoked(object sender, EventArgs e)
        {
            VirtualInterface interf = (VirtualInterface)sender;
            VirtualAdapter adapter = interf.GetAdapter();
            TS_STATES state = interf.GetState();

            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                String entryName = ArrayGetComboBoxText(i);

                if (adapter.Equals(entryName))
                {
                    Boolean enabled;
                    switch (state)
                    {
                        case TS_STATES.Connected:
                        case TS_STATES.Disconnected:
                        case TS_STATES.Dialing:
                            enabled = true;
                            break;
                        case TS_STATES.Disconnecting:
                        case TS_STATES.Unknown:
                            enabled = false;
                            break;
                        default:
                            enabled = false;
                            break;
                    }

                    if (adapter.GetDeviceType() == TS_ADAPTER_TYPES.NetworkAdapter)
                        enabled = false;

                    ArraySetButtonText(i, Constants.ADAPTER_ACTIONS[(Int32)state]);
                    ArraySetButtonEnabled(i, enabled);

                    // Explicitly continue for all interfaces
                }
            }

            // Automatic redial feature
            if (listOfReconnectCheckboxes[(Int32)interf.GetInterfaceType()].Checked && (state == TS_STATES.Disconnected))
                interf.Dial();

            // Automatic route setup feature
            if (chkAutoRouteSetup.Checked && (state == TS_STATES.Connected))
                SetupRoute(interf.GetInterfaceType());

            // DynDNS
            if ((interf.GetInterfaceType() == TS_INTERFACES.Local) && (state == TS_STATES.Connected) && ddns.IsActive())
                ddns.Update(true);
        }

        private void adapterStateChanged(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                StateChanged cb = new StateChanged(StateChangedInvoked);
                this.Invoke(cb, new object[] {sender, e});
            }
            else
                StateChangedInvoked(sender, e);
        }

        private void errorOnConnect(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                OnConnectError cb = new OnConnectError(errorOnConnectInvoked);
                this.Invoke(cb, new object[] {sender, e});
            }
            else
                errorOnConnectInvoked(sender, e);
        }

        private void errorOnConnectInvoked(object sender, EventArgs e)
        {
            VirtualInterface interf = (VirtualInterface)sender;
            
            // Disable automatically reconnect
            listOfReconnectCheckboxes[(Int32)interf.GetInterfaceType()].Checked = false;
        }

        private void ActionButtonClicked(object sender, EventArgs e)
        {
            Int32 i = ArrayGetIndex((Button)sender);
            VirtualInterface interf = interfaces[i];
            TS_STATES state = interf.GetState();

            if (state == TS_STATES.Disconnected)
                interf.Dial();
            else if (state == TS_STATES.Connected)
                interf.Disconnect();
            else if (state == TS_STATES.Dialing)
                interf.DialCancel();
        }

        private void ShowRoutes(object sender, EventArgs e)
        {
            int i = ArrayGetIndex((Button)sender);
            Routes frm = new Routes(Constants.INTERFACE_NAMES[i] + " routes", interfaces[i].GetRoutes());
            frm.ShowDialog();
            frm = null;
        }

        private void btnSetupRoutes_Click(object sender, EventArgs e)
        {
            Boolean waiting = false;
            
            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                if (interfaces[i].GetAdapter() == null)
                    continue;

                if (interfaces[i].GetAdapter().GetDeviceType() != TS_ADAPTER_TYPES.None)
                {
                    if (interfaces[i].GetState() != TS_STATES.Connected)
                        waiting = true;
                }
            }

            if (!waiting)
                SetupRoute(TS_INTERFACES.All);
            else
                MessageBox.Show("Please connect all selected interfaces before setting up the routes.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (config.HasChanged())
            {
                DialogResult res = MessageBox.Show("Do you want to save changes?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (res == DialogResult.Yes)
                    config.Save();
                else if (res == DialogResult.Cancel)
                    e.Cancel = true;
            }
        }

        private void btnUpdateRoutes_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            String tmp = btn.Text;
            btn.Text = "Downloading...";
            btn.Enabled = false;
            Application.DoEvents();
            WebClient Client = new WebClient();
            Client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
            try
            {
                if (btn == btnUpdateRoutes)
                {
                    Client.DownloadFile(Constants.LOCAL_ROUTES_URL[ArrayGetIndex(rbLocalListLocalityDevs)], Application.StartupPath + "\\localroutes-new.dat");
                    File.Replace(Application.StartupPath + "\\localroutes-new.dat", Application.StartupPath + "\\localroutes.dat", Application.StartupPath + "\\localroutes.bak");
                    interfaces[(Int32)TS_INTERFACES.Local].ImportRoutes();
                    MessageBox.Show("Local Route list downloaded & imported successfully.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    Client.DownloadFile(Constants.AUXILARY_ROUTES_URL[ArrayGetIndex(rbAuxListMweb)], Application.StartupPath + "\\auxroutes-new.dat");
                    int index = cbAuxRouteSelector.SelectedIndex + 2;
                    File.Replace(Application.StartupPath + "\\auxroutes-new.dat", Application.StartupPath + "\\" + Constants.FILENAMES[index], Application.StartupPath + "\\" + Constants.FILENAMES[index] + ".bak");
                    interfaces[index].ImportRoutes();
                    MessageBox.Show(Constants.INTERFACE_NAMES[index] + " Route list downloaded & imported successfully.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (WebException ex)
            {
                MessageBox.Show (ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (NotSupportedException ex)
            {
                MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            btn.Text = tmp;
            btn.Enabled = true;
        }

        private void btnImportRoutes_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                String name = ((TS_INTERFACES)i).ToString();
                AppendToLog(LOG_TYPE.Debug, "Importing " + name + " Routes");
                interfaces[i].ImportRoutes();
            }
            MessageBox.Show("Imported Routes.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void btnEditRoutes_Click(object sender, EventArgs e)
        {
            Int32 index = ArrayGetIndex((Button)sender);
            String name = ((TS_INTERFACES)index).ToString();
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.FileName = "write";
            p.StartInfo.Arguments = "\"" + Application.StartupPath + "\\" + Constants.FILENAMES[index] + "\"";
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.StandardErrorEncoding = Encoding.ASCII;
            p.StartInfo.CreateNoWindow = true;
            try
            {
                p.Start();
            }
            catch (Win32Exception ex)
            {
                if (ex.NativeErrorCode == 2)
                {
                    // Cannot find specified file
                    p.StartInfo.FileName = "notepad";
                    p.Start();
                }
            }
            AppendToLog(LOG_TYPE.Debug, "Editing " + name + " Routes");
        }


        private void btnShowIP_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            String url;
            Label lbl;

            if (btn == btnShowIntIP)
            {
                url = Constants.GET_INT_IP;
                lbl = lblIntIP;
            }
            else
            {
                url = Constants.GET_LOCAL_IP;
                lbl = lblLocalIP;
            }

            String tmp = btn.Text;
            btn.Text = "Updating...";
            lbl.Text = "from " + url;
            btn.Enabled = false;
            Application.DoEvents();
            WebClient Client = new WebClient();
            Client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
            string result = string.Empty;
            try
            {
                Action<string> action = new Action<string>((urlstream) =>
                {
                    Stream strm = Client.OpenRead(urlstream);
                    StreamReader sr = new StreamReader(strm);
                    string line;
                    do
                    {
                        line = sr.ReadLine();
                        if (line != null)
                        {
                            Match ret = Constants.ipPattern.Match(line);
                            if (ret.Success)
                                result = ret.Value;
                        }
                    }
                    while (line != null);
                    strm.Close();
                });

                action.BeginInvoke(url, new AsyncCallback(a =>
                {
                    this.Invoke(new MethodInvoker(() => { lbl.Text = result; }));
                }), null);
            }
            catch (WebException ex)
            {
                MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (NotSupportedException ex)
            {
                MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            btn.Enabled = true;
            btn.Text = tmp;
        }

        #endregion

        #region Functions
        private void SetupRoute(TS_INTERFACES interf)
        {
            int index = (Int32)interf;
            int start;
            int end;

            if (interf == TS_INTERFACES.All)
            {
                start = 0;
                end = Constants.MAX_INTERFACES;
            }
            else
            {
                start = index;
                end = index + 1;
            }

            if (btnSetupRoutes.Text.Equals("Abort Route Setup", StringComparison.OrdinalIgnoreCase))
            {
                Constants.AppendToLog(LOG_TYPE.Error, "Aborting Route Setup");
                continueSetupRoutes = false;
                for (int i = start; i < end; i++)
                {
                    interfaces[i].AbortRouteAddition();
                }
            }
            else
            {
                chkAutoRouteSetup.Enabled = false;
                String tmp = btnSetupRoutes.Text;
                btnSetupRoutes.Text = "Abort Route Setup";
                rbAdd.Enabled = false;
                rbDel.Enabled = false;
                Constants.AppendToLog(LOG_TYPE.Info, "Setting up routes");
                Boolean addOrDelete = rbAdd.Checked;

                for (int i = start; i < end && continueSetupRoutes; i++)
                {
                    listOfComboboxes[i].Enabled = false;
                    listOfPersistentCheckboxes[i].Enabled = false;
                    try
                    {
                        interfaces[i].SetupRoutes(addOrDelete, listOfPersistentCheckboxes[i].Checked);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    listOfPersistentCheckboxes[i].Enabled = true;
                    listOfComboboxes[i].Enabled = true;
                }
                
                continueSetupRoutes = true;
                rbAdd.Enabled = true;
                rbDel.Enabled = true;
                chkAutoRouteSetup.Enabled = true;
                btnSetupRoutes.Text = tmp;
            }
        }
        #endregion

        private void Main_Resize(object sender, EventArgs e)
        {
            if ((chkMinimize2Tray.Checked) && (FormWindowState.Minimized == WindowState))  
            {
                this.notifyIcon1.Visible = true;
                this.ShowInTaskbar = false;
                this.Hide();
                if (showBalloon)
                {
                    notifyIcon1.ShowBalloonTip(1000);
                    showBalloon = false;
                    config.Save(showBalloon);
                }
            }  
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;  
            this.ShowInTaskbar = true;
            this.notifyIcon1.Visible = false;
        }

        private void Main_Load(object sender, EventArgs e)
        {
            Application.Idle += new EventHandler(OnLoaded);

            // Minimize on Start
            config.LoadMinimizing();
            if (chkMinimizeOnStartup.Checked == true)
                this.WindowState = FormWindowState.Minimized;
        }

        private void OnLoaded(object sender, EventArgs args)
        {
            Application.Idle -= new EventHandler(OnLoaded);

            // Load settings
            config.Load();
            config.Load(ref showBalloon);
            config.Load(listOfStatsValues);

            // Load DynDNS
            ddns.LoadSettings();

            // Update stats
            btnUpdateStats_Click(null, null);

            // Start connection updates
            timerConnectionStatus.Enabled = true;
        }

        private void btnResetStats_Click(object sender, EventArgs e)
        {
            DialogResult res = MessageBox.Show("Are you sure you want to clear the stats?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            if (res == DialogResult.No)
                return;

            for (int type = 0; type < 4; type++)
            {
                for (int i = 0; i < Constants.MAX_INTERFACES; i++)
                {
                    listOfStatsValues[type][i] = 0;
                }
            }

            btnUpdateStats_Click(sender, e);
        }

        private void btnUpdateStats_Click(object sender, EventArgs e)
        {
            long recv;
            long sent;
            long recvInc;
            long sentInc;
            for (int i = 0; i < Constants.MAX_INTERFACES; i++)
            {
                if ((interfaces[i].GetAdapter() != null) && (interfaces[i].GetAdapter().GetDeviceType() != TS_ADAPTER_TYPES.None) && (interfaces[i].GetState() == TS_STATES.Connected))
                {
                    Routing.GetStats(interfaces[i].GetAdapter(), out recv, out sent);
                    if (listOfStatsValues[2][i] == 0)
                    {
                        recvInc = 0;
                        sentInc = 0;
                    }
                    else if ((listOfStatsValues[2][i] > recv) || (listOfStatsValues[3][i] > sent))
                    {
                        recvInc = 0;
                        sentInc = 0;
                    }
                    else
                    {
                        recvInc = recv - listOfStatsValues[2][i];
                        sentInc = sent - listOfStatsValues[3][i];
                    }
                    listOfStatsValues[2][i] = recv;
                    listOfStatsValues[3][i] = sent;

                    listOfStatsValues[0][i] += recvInc;
                    listOfStatsValues[1][i] += sentInc;

                    listOfStatsLabels[0][i].Text = FormatStats(recv) + " - Accumulated: " + FormatStats(listOfStatsValues[0][i]);
                    listOfStatsLabels[1][i].Text = FormatStats(sent) + " - Accumulated: " + FormatStats(listOfStatsValues[1][i]);
                }
                else
                {
                    listOfStatsValues[2][i] = 0;
                    listOfStatsValues[3][i] = 0;
                    listOfStatsLabels[0][i].Text = FormatStats(0) + " - Accumulated: " + FormatStats(listOfStatsValues[0][i]);
                    listOfStatsLabels[1][i].Text = FormatStats(0) + " - Accumulated: " + FormatStats(listOfStatsValues[1][i]);
                }
            }
            config.Save(listOfStatsValues);
        }

        private String FormatStats(long bytes)
        {
            const long SIZE_KB = 1024;
            const long SIZE_MB = 1048576;
            const long SIZE_GB = 1073741824;

            if (bytes <= 1024)
                return String.Format("{0:#,0} bytes", bytes);
            else if (bytes > SIZE_GB)
                return String.Format("{0:#,#.00} GB", (double)bytes / SIZE_GB);
            else if (bytes > SIZE_MB)
                return String.Format("{0:#,#.00} MB", (double)bytes / SIZE_MB);
            else
                return String.Format("{0:#,#.00} KB", (double)bytes / SIZE_KB);
        }

        private void timerStats_Tick(object sender, EventArgs e)
        {
            btnUpdateStats_Click(null, null);
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            LinkLabel ll = (LinkLabel)sender;
            String URL;
            if (ll == linkLabel1)
            {
                URL = ll.Text;
            }
            else
            {
                URL = "http://mybroadband.co.za/news/broadband/16313-MWEB-peering-link-cuts-How-impacts-you.html";
            }
            try
            {
                System.Diagnostics.Process.Start(URL);
            }
            catch (Win32Exception ex)
            {
                AppendToLog(LOG_TYPE.Error, ex.Message);
                MessageBox.Show("Unable to open hyperlink: " + URL, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnCheckUpdate_Click(object sender, EventArgs e)
        {
            Application.DoEvents();
            WebClient Client = new WebClient();
            Client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
            String filename = Application.StartupPath + "\\version-info";
                
            try
            {
                File.Delete(filename);
            }
            catch (Exception)
            {
            }
            try
            {
                Client.DownloadFile(Constants.UPDATE_URL, filename);
                String ver = config.LoadVersion(filename);
                String[] tmp = ver.Split('.');
                String[] tmp2 = Application.ProductVersion.Split('.');
                Int16[] num = new Int16[tmp.Length];
                Int16[] num2 = new Int16[tmp2.Length];
                for (int i = 0; i < tmp.Length; i++)
                {
                    num[i] = Convert.ToInt16(tmp[i]);
                }
                for (int i = 0; i < tmp2.Length; i++)
                {
                    num2[i] = Convert.ToInt16(tmp2[i]);
                }
                for (int i = 0; (i < tmp2.Length) && (i < tmp.Length); i++)
                {
                    if (num2[i] < num[i])
                    {

                        DialogResult res = MessageBox.Show("New version is available: " + ver + "\nYou have: " + Application.ProductVersion + "\n\nPress OK to open download page.", Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
                        if (res == DialogResult.OK)
                        {
                            try
                            {
                                System.Diagnostics.Process.Start(Constants.UPDATE_PAGE);
                            }
                            catch (Win32Exception ex)
                            {
                                AppendToLog(LOG_TYPE.Error, ex.Message);
                                MessageBox.Show("Unable to open hyperlink: " + Constants.UPDATE_PAGE, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                        }
                        return;
                    }
                }


                MessageBox.Show("Your version is up to date.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (WebException ex)
            {
                MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (NotSupportedException ex)
            {
                MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void cmdSave_Click(object sender, EventArgs e)
        {
            config.Save();
        }

        private void btnDDNSUdate_Click(object sender, EventArgs e)
        {
            btnDDNSUdate.Enabled = false;
            ddns.Update();
            btnDDNSUdate.Enabled = ddns.IsActive();
        }

        private void btnSaveDDNS_Click(object sender, EventArgs e)
        {
            btnDDNSSave.Enabled = false;
            ddns.SaveSettings();
            btnDDNSSave.Enabled = true;
        }

        private void chkDDNSEnabled_CheckedChanged(object sender, EventArgs e)
        {
            ddns.UpdateControls();
        }

        private void DDNSInputChanged(object sender, EventArgs e)
        {
            ddns.InputChanged(sender, e);
        }

        private void btnDDNSLoad_Click(object sender, EventArgs e)
        {
            btnDDNSLoad.Enabled = false;
            ddns.LoadSettings();
            btnDDNSLoad.Enabled = true;
        }

        private void btnClearLogs_Click(object sender, EventArgs e)
        {
            tbLogs.Clear();
        }

        private void timerConnectionStatus_Tick(object sender, EventArgs e)
        {
            for (int i = 0; i < interfaces.Length; i++)
            {
                interfaces[i].RefreshState();
            }
        }
    }
}
